Preamble

# Clear workspace
rm(list=ls()); graphics.off() 
### Load packages
library(tidyverse) # Collection of all the good stuff like dplyr, ggplot2 ect.
library(magrittr) # For extra-piping operators (eg. %<>%)
library(skimr) # For nice data summaries

The InsideAirBnB data

Instroduction

  • The data is sourced from the Inside Airbnb which hosts publicly available data from the Airbnb site.
  • Interactive visualizations are provided here

The dataset comprises of three main tables:

  • listings - Detailed listings data showing 96 atttributes for each of the listings. Some of the attributes which are intuitivly interesting are: price (continuous), longitude (continuous), latitude (continuous), listing_type (categorical), is_superhost (categorical), neighbourhood (categorical), ratings (continuous) among others.
  • reviews - Detailed reviews given by the guests with 6 attributes. Key attributes include date (datetime), listing_id (discrete), reviewer_id (discrete) and comment (textual).
  • calendar - Provides details about booking for the next year by listing. Four attributes in total including listing_id (discrete), date (datetime), available (categorical) and price (continuous).

Load data

listings <- read_csv('http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/data/listings.csv.gz')
listings %>% head()
calendar <- read_csv('http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/data/calendar.csv.gz')
calendar %>% head()
reviews <- read_csv('http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/data/reviews.csv.gz')
reviews %>% head()
# The geodat of the hoods comes as a geojson, so we need the right package to load it
library(geojsonio)
neighbourhoods_geojson <- geojson_read( 'http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/visualisations/neighbourhoods.geojson',  what = "sp")

Some preprocessing

listings %<>% mutate(price = price %>% parse_number()) 

Ad-hoc investigation

Problem 1: Professional hosts & their characteristics

listings %>%
  count(host_id, sort = TRUE)

Where are they?

listings %>%
  filter(host_id == 187610263) %>%
  count(neighbourhood_cleansed, sort = TRUE)

Dummy for professional host

listings %<>%
  group_by(host_id) %>%
  mutate(host_professional = n() >= 5) %>%
  ungroup()
listings %>%
  group_by(host_professional) %>%
  summarise(review = review_scores_rating %>% mean(na.rm = TRUE),
            price = price %>% mean(na.rm = TRUE))

-> Profressional hosts charge more…

listings %>%
  group_by(neighbourhood_cleansed, host_professional) %>%
  summarise(review = review_scores_rating %>% mean(na.rm = TRUE)) %>%
  pivot_wider(names_from = host_professional, values_from = review)

-> This is true everywhere, yet in some hoods mnore tghan in others

Problem 2: Length of description -> Satisfaction

listings %<>%
  mutate(desc_lenght = description %>% str_count('\\w+')) %>%
  mutate(desc_long =  percent_rank(desc_lenght) > 0.9 )
listings %>%
  group_by(desc_long) %>%
  summarise(review = review_scores_rating %>% mean(na.rm =TRUE))

-> No overall effect

P3: Best party place

listings %<>% 
  mutate(party_place = accommodates >= 10) 
listings %>% 
  filter(party_place == TRUE) %>%
  group_by(neighbourhood_cleansed) %>%
  summarize(n = n(),
         review = review_scores_rating %>% mean(na.rm = TRUE),
         price = price %>% mean(na.rm = TRUE),
         price_pp = (price / accommodates) %>% mean(na.rm = TRUE)) %>%
  arrange(desc(n))

If you are on a tight budget, best go to Amager-Vest.

Geoplotting

Interactive map

library(leaflet)
listings %>% leaflet() %>%
  addTiles() %>%
  addMarkers(~longitude, ~latitude,
             labelOptions = labelOptions(noHide = F),
             clusterOptions = markerClusterOptions(),
             popup = paste0("<b> Name: </b>", listings$name, 
                            "<br/><b> Host Name: </b>", listings$host_name, 
                            "<br> <b> Price: </b>", listings$price, 
                            "<br/><b> Room Type: </b>", listings$room_type, 
                            "<br/><b> Property Type: </b>", listings$property_type
                 )) %>% 
#  setView(-74.00, 40.71, zoom = 12) %>%
  addProviderTiles("CartoDB.Positron")

Choropleth Plots

# Using broom to tidy the geojson
library(broom)
neighbourhoods_tidy <-  neighbourhoods_geojson %>%
  tidy(region = "neighbourhood")
neighbourhoods_tidy %>% glimpse()
Rows: 6,658
Columns: 7
$ long  <dbl> 12.63094, 12.63126, 12.63221, 12.63160, 12.63154, 12.63153, 12.63153, 12.63153, 12.63157, 12.63158, 12.6…
$ lat   <dbl> 55.67050, 55.67028, 55.66961, 55.66943, 55.66941, 55.66940, 55.66939, 55.66930, 55.66926, 55.66924, 55.6…
$ order <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 2…
$ hole  <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ piece <fct> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
$ group <fct> Amager st.1, Amager st.1, Amager st.1, Amager st.1, Amager st.1, Amager st.1, Amager st.1, Amager st.1, …
$ id    <chr> "Amager st", "Amager st", "Amager st", "Amager st", "Amager st", "Amager st", "Amager st", "Amager st", …
neighbourhoods_tidy %>%
  ggplot(aes(x = long, y = lat, group = group)) +
  geom_polygon() +
  theme_void() +
  coord_map()

neighborhood_agg <- listings %>%
  group_by(neighbourhood_cleansed) %>%
  summarise(n = n(),
            price_mean = price %>% mean(na.rm = TRUE),
            review_mean = review_scores_rating %>% mean(na.rm = TRUE))
  
neighbourhoods_tidy %<>%
  left_join(neighborhood_agg, by = c('id' = 'neighbourhood_cleansed'))

Number of places

neighbourhoods_tidy %>%
  ggplot(aes(x = long, y = lat, group = group, fill = n)) +
  geom_polygon() +
  theme_void() +
  coord_map()

Prices

neighbourhoods_tidy %>%
  ggplot(aes(x = long, y = lat, group = group, fill = price_mean)) +
  geom_polygon() +
  theme_void() +
  coord_map()

Review scores

neighbourhoods_tidy %>%
  ggplot(aes(x = long, y = lat, group = group, fill = review_mean)) +
  geom_polygon() +
  theme_void() +
  coord_map()

LS0tCnRpdGxlOiAiV29ya3Nob3A6IEV4cGxvcmluZyB0aGUgSW5zaWRlQWlyQm5CIGRhdGFzZXQgLSBFREEiCmF1dGhvcjogIkRhbmllbCBTLiBIYWluIChkc2hAYnVzaW5lc3MuYWF1LmRrKSIKZGF0ZTogIlVwZGF0ZWQgYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiAlZCwgJVknKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIHRoZW1lOiBmbGF0bHkKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBLbml0ciBvcHRpb25zCiMjIyBHZW5lcmljIHByZWFtYmxlClN5cy5zZXRlbnYoTEFORyA9ICJlbiIpICMgRm9yIGVuZ2xpc2ggbGFuZ3VhZ2UKb3B0aW9ucyhzY2lwZW4gPSA1KSAjIFRvIGRlYWN0aXZhdGUgYW5ub3lpbmcgc2NpZW50aWZpYyBudW1iZXIgbm90YXRpb24KCiMgcm0obGlzdD1scygpKTsgZ3JhcGhpY3Mub2ZmKCkgIyBnZXQgcmlkIG9mIGV2ZXJ5dGhpbmcgaW4gdGhlIHdvcmtzcGFjZQppZiAoIXJlcXVpcmUoImtuaXRyIikpIGluc3RhbGwucGFja2FnZXMoImtuaXRyIik7IGxpYnJhcnkoa25pdHIpICMgRm9yIGRpc3BsYXkgb2YgdGhlIG1hcmtkb3duCgojIyMgS25pdHIgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZz1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZT1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduPSJjZW50ZXIiCiAgICAgICAgICAgICAgICAgICAgICkKYGBgCgojIyBQcmVhbWJsZQoKYGBge3J9CiMgQ2xlYXIgd29ya3NwYWNlCnJtKGxpc3Q9bHMoKSk7IGdyYXBoaWNzLm9mZigpIApgYGAKCmBgYHtyfQojIyMgTG9hZCBwYWNrYWdlcwpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBDb2xsZWN0aW9uIG9mIGFsbCB0aGUgZ29vZCBzdHVmZiBsaWtlIGRwbHlyLCBnZ3Bsb3QyIGVjdC4KbGlicmFyeShtYWdyaXR0cikgIyBGb3IgZXh0cmEtcGlwaW5nIG9wZXJhdG9ycyAoZWcuICU8PiUpCmxpYnJhcnkoc2tpbXIpICMgRm9yIG5pY2UgZGF0YSBzdW1tYXJpZXMKYGBgCgoKIyBUaGUgSW5zaWRlQWlyQm5CIGRhdGEKCiMjIEluc3Ryb2R1Y3Rpb24KCgoqIFRoZSBkYXRhIGlzIHNvdXJjZWQgZnJvbSB0aGUgWyoqSW5zaWRlIEFpcmJuYioqXShodHRwOi8vaW5zaWRlYWlyYm5iLmNvbS9nZXQtdGhlLWRhdGEuaHRtbCkgd2hpY2ggaG9zdHMgcHVibGljbHkgYXZhaWxhYmxlIGRhdGEgZnJvbSB0aGUgQWlyYm5iIHNpdGUuCiogSW50ZXJhY3RpdmUgdmlzdWFsaXphdGlvbnMgYXJlIHByb3ZpZGVkIFtoZXJlXShodHRwOi8vaW5zaWRlYWlyYm5iLmNvbS9jb3BlbmhhZ2VuLz9uZWlnaGJvdXJob29kPSZmaWx0ZXJFbnRpcmVIb21lcz1mYWxzZSZmaWx0ZXJIaWdobHlBdmFpbGFibGU9ZmFsc2UmZmlsdGVyUmVjZW50UmV2aWV3cz1mYWxzZSZmaWx0ZXJNdWx0aUxpc3RpbmdzPWZhbHNlKQoKVGhlIGRhdGFzZXQgY29tcHJpc2VzIG9mIHRocmVlIG1haW4gdGFibGVzOgoKKiBgbGlzdGluZ3NgIC0gRGV0YWlsZWQgbGlzdGluZ3MgZGF0YSBzaG93aW5nIDk2IGF0dHRyaWJ1dGVzIGZvciBlYWNoIG9mIHRoZSBsaXN0aW5ncy4gU29tZSBvZiB0aGUgYXR0cmlidXRlcyB3aGljaCBhcmUgaW50dWl0aXZseSBpbnRlcmVzdGluZyBhcmU6IGBwcmljZWAgKGNvbnRpbnVvdXMpLCBgbG9uZ2l0dWRlYCAoY29udGludW91cyksIGBsYXRpdHVkZWAgKGNvbnRpbnVvdXMpLCBgbGlzdGluZ190eXBlYCAoY2F0ZWdvcmljYWwpLCBgaXNfc3VwZXJob3N0YCAoY2F0ZWdvcmljYWwpLCBgbmVpZ2hib3VyaG9vZGAgKGNhdGVnb3JpY2FsKSwgYHJhdGluZ3NgIChjb250aW51b3VzKSBhbW9uZyBvdGhlcnMuCiogYHJldmlld3NgIC0gRGV0YWlsZWQgcmV2aWV3cyBnaXZlbiBieSB0aGUgZ3Vlc3RzIHdpdGggNiBhdHRyaWJ1dGVzLiBLZXkgYXR0cmlidXRlcyBpbmNsdWRlIGBkYXRlYCAoZGF0ZXRpbWUpLCBgbGlzdGluZ19pZGAgKGRpc2NyZXRlKSwgYHJldmlld2VyX2lkYCAoZGlzY3JldGUpIGFuZCBgY29tbWVudGAgKHRleHR1YWwpLgoqIGBjYWxlbmRhcmAgLSBQcm92aWRlcyBkZXRhaWxzIGFib3V0IGJvb2tpbmcgZm9yIHRoZSBuZXh0IHllYXIgYnkgbGlzdGluZy4gRm91ciBhdHRyaWJ1dGVzIGluIHRvdGFsIGluY2x1ZGluZyBgbGlzdGluZ19pZGAgKGRpc2NyZXRlKSwgYGRhdGVgIChkYXRldGltZSksIGBhdmFpbGFibGVgIChjYXRlZ29yaWNhbCkgYW5kIGBwcmljZWAgKGNvbnRpbnVvdXMpLgoKIyMgTG9hZCBkYXRhCgpgYGB7cn0KbGlzdGluZ3MgPC0gcmVhZF9jc3YoJ2h0dHA6Ly9kYXRhLmluc2lkZWFpcmJuYi5jb20vZGVubWFyay9ob3ZlZHN0YWRlbi9jb3BlbmhhZ2VuLzIwMjAtMDYtMjYvZGF0YS9saXN0aW5ncy5jc3YuZ3onKQpsaXN0aW5ncyAlPiUgaGVhZCgpCmBgYAoKYGBge3J9CmNhbGVuZGFyIDwtIHJlYWRfY3N2KCdodHRwOi8vZGF0YS5pbnNpZGVhaXJibmIuY29tL2Rlbm1hcmsvaG92ZWRzdGFkZW4vY29wZW5oYWdlbi8yMDIwLTA2LTI2L2RhdGEvY2FsZW5kYXIuY3N2Lmd6JykKY2FsZW5kYXIgJT4lIGhlYWQoKQpgYGAKCmBgYHtyfQpyZXZpZXdzIDwtIHJlYWRfY3N2KCdodHRwOi8vZGF0YS5pbnNpZGVhaXJibmIuY29tL2Rlbm1hcmsvaG92ZWRzdGFkZW4vY29wZW5oYWdlbi8yMDIwLTA2LTI2L2RhdGEvcmV2aWV3cy5jc3YuZ3onKQpyZXZpZXdzICU+JSBoZWFkKCkKYGBgCgpgYGB7cn0KIyBUaGUgZ2VvZGF0IG9mIHRoZSBob29kcyBjb21lcyBhcyBhIGdlb2pzb24sIHNvIHdlIG5lZWQgdGhlIHJpZ2h0IHBhY2thZ2UgdG8gbG9hZCBpdApsaWJyYXJ5KGdlb2pzb25pbykKbmVpZ2hib3VyaG9vZHNfZ2VvanNvbiA8LSBnZW9qc29uX3JlYWQoICdodHRwOi8vZGF0YS5pbnNpZGVhaXJibmIuY29tL2Rlbm1hcmsvaG92ZWRzdGFkZW4vY29wZW5oYWdlbi8yMDIwLTA2LTI2L3Zpc3VhbGlzYXRpb25zL25laWdoYm91cmhvb2RzLmdlb2pzb24nLCAgd2hhdCA9ICJzcCIpCmBgYAoKIyMgU29tZSBwcmVwcm9jZXNzaW5nCgpgYGB7cn0KbGlzdGluZ3MgJTw+JSBtdXRhdGUocHJpY2UgPSBwcmljZSAlPiUgcGFyc2VfbnVtYmVyKCkpIApgYGAKCiMgQWQtaG9jIGludmVzdGlnYXRpb24KCiMjIFByb2JsZW0gMTogUHJvZmVzc2lvbmFsIGhvc3RzICYgdGhlaXIgY2hhcmFjdGVyaXN0aWNzCgpgYGB7cn0KbGlzdGluZ3MgJT4lCiAgY291bnQoaG9zdF9pZCwgc29ydCA9IFRSVUUpCmBgYAoKV2hlcmUgYXJlIHRoZXk/CgpgYGB7cn0KbGlzdGluZ3MgJT4lCiAgZmlsdGVyKGhvc3RfaWQgPT0gMTg3NjEwMjYzKSAlPiUKICBjb3VudChuZWlnaGJvdXJob29kX2NsZWFuc2VkLCBzb3J0ID0gVFJVRSkKYGBgCkR1bW15IGZvciBwcm9mZXNzaW9uYWwgaG9zdApgYGB7cn0KbGlzdGluZ3MgJTw+JQogIGdyb3VwX2J5KGhvc3RfaWQpICU+JQogIG11dGF0ZShob3N0X3Byb2Zlc3Npb25hbCA9IG4oKSA+PSA1KSAlPiUKICB1bmdyb3VwKCkKYGBgCgpgYGB7cn0KbGlzdGluZ3MgJT4lCiAgZ3JvdXBfYnkoaG9zdF9wcm9mZXNzaW9uYWwpICU+JQogIHN1bW1hcmlzZShyZXZpZXcgPSByZXZpZXdfc2NvcmVzX3JhdGluZyAlPiUgbWVhbihuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBwcmljZSA9IHByaWNlICU+JSBtZWFuKG5hLnJtID0gVFJVRSkpCmBgYAotPiBQcm9mcmVzc2lvbmFsIGhvc3RzIGNoYXJnZSBtb3JlLi4uCgpgYGB7cn0KbGlzdGluZ3MgJT4lCiAgZ3JvdXBfYnkobmVpZ2hib3VyaG9vZF9jbGVhbnNlZCwgaG9zdF9wcm9mZXNzaW9uYWwpICU+JQogIHN1bW1hcmlzZShyZXZpZXcgPSByZXZpZXdfc2NvcmVzX3JhdGluZyAlPiUgbWVhbihuYS5ybSA9IFRSVUUpKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaG9zdF9wcm9mZXNzaW9uYWwsIHZhbHVlc19mcm9tID0gcmV2aWV3KQpgYGAKCi0+IFRoaXMgaXMgdHJ1ZSBldmVyeXdoZXJlLCB5ZXQgaW4gc29tZSBob29kcyBtbm9yZSB0Z2hhbiBpbiBvdGhlcnMKCiMjIFByb2JsZW0gMjogTGVuZ3RoIG9mIGRlc2NyaXB0aW9uIC0+IFNhdGlzZmFjdGlvbgoKYGBge3J9Cmxpc3RpbmdzICU8PiUKICBtdXRhdGUoZGVzY19sZW5naHQgPSBkZXNjcmlwdGlvbiAlPiUgc3RyX2NvdW50KCdcXHcrJykpICU+JQogIG11dGF0ZShkZXNjX2xvbmcgPSAgcGVyY2VudF9yYW5rKGRlc2NfbGVuZ2h0KSA+IDAuOSApCmBgYAoKYGBge3J9Cmxpc3RpbmdzICU+JQogIGdyb3VwX2J5KGRlc2NfbG9uZykgJT4lCiAgc3VtbWFyaXNlKHJldmlldyA9IHJldmlld19zY29yZXNfcmF0aW5nICU+JSBtZWFuKG5hLnJtID1UUlVFKSkKYGBgCi0+IE5vIG92ZXJhbGwgZWZmZWN0CgojIyBQMzogQmVzdCBwYXJ0eSBwbGFjZQoKYGBge3J9Cmxpc3RpbmdzICU8PiUgCiAgbXV0YXRlKHBhcnR5X3BsYWNlID0gYWNjb21tb2RhdGVzID49IDEwKSAKYGBgCgpgYGB7cn0KbGlzdGluZ3MgJT4lIAogIGZpbHRlcihwYXJ0eV9wbGFjZSA9PSBUUlVFKSAlPiUKICBncm91cF9ieShuZWlnaGJvdXJob29kX2NsZWFuc2VkKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSwKICAgICAgICAgcmV2aWV3ID0gcmV2aWV3X3Njb3Jlc19yYXRpbmcgJT4lIG1lYW4obmEucm0gPSBUUlVFKSwKICAgICAgICAgcHJpY2UgPSBwcmljZSAlPiUgbWVhbihuYS5ybSA9IFRSVUUpLAogICAgICAgICBwcmljZV9wcCA9IChwcmljZSAvIGFjY29tbW9kYXRlcykgJT4lIG1lYW4obmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShkZXNjKG4pKQpgYGAKCklmIHlvdSBhcmUgb24gYSB0aWdodCBidWRnZXQsIGJlc3QgZ28gdG8gQW1hZ2VyLVZlc3QuCgojIEdlb3Bsb3R0aW5nCgojIyBJbnRlcmFjdGl2ZSBtYXAKCmBgYHtyfQpsaWJyYXJ5KGxlYWZsZXQpCmBgYAoKYGBge3J9Cmxpc3RpbmdzICU+JSBsZWFmbGV0KCkgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRNYXJrZXJzKH5sb25naXR1ZGUsIH5sYXRpdHVkZSwKICAgICAgICAgICAgIGxhYmVsT3B0aW9ucyA9IGxhYmVsT3B0aW9ucyhub0hpZGUgPSBGKSwKICAgICAgICAgICAgIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSwKICAgICAgICAgICAgIHBvcHVwID0gcGFzdGUwKCI8Yj4gTmFtZTogPC9iPiIsIGxpc3RpbmdzJG5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxici8+PGI+IEhvc3QgTmFtZTogPC9iPiIsIGxpc3RpbmdzJGhvc3RfbmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPiA8Yj4gUHJpY2U6IDwvYj4iLCBsaXN0aW5ncyRwcmljZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyLz48Yj4gUm9vbSBUeXBlOiA8L2I+IiwgbGlzdGluZ3Mkcm9vbV90eXBlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnIvPjxiPiBQcm9wZXJ0eSBUeXBlOiA8L2I+IiwgbGlzdGluZ3MkcHJvcGVydHlfdHlwZQogICAgICAgICAgICAgICAgICkpICU+JSAKIyAgc2V0VmlldygtNzQuMDAsIDQwLjcxLCB6b29tID0gMTIpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMoIkNhcnRvREIuUG9zaXRyb24iKQpgYGAKCiMjIENob3JvcGxldGggUGxvdHMKCmBgYHtyfQojIFVzaW5nIGJyb29tIHRvIHRpZHkgdGhlIGdlb2pzb24KbGlicmFyeShicm9vbSkKbmVpZ2hib3VyaG9vZHNfdGlkeSA8LSAgbmVpZ2hib3VyaG9vZHNfZ2VvanNvbiAlPiUKICB0aWR5KHJlZ2lvbiA9ICJuZWlnaGJvdXJob29kIikKYGBgCgpgYGB7cn0KbmVpZ2hib3VyaG9vZHNfdGlkeSAlPiUgZ2xpbXBzZSgpCmBgYAoKYGBge3J9Cm5laWdoYm91cmhvb2RzX3RpZHkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpICsKICBnZW9tX3BvbHlnb24oKSArCiAgdGhlbWVfdm9pZCgpICsKICBjb29yZF9tYXAoKQpgYGAKYGBge3J9Cm5laWdoYm9yaG9vZF9hZ2cgPC0gbGlzdGluZ3MgJT4lCiAgZ3JvdXBfYnkobmVpZ2hib3VyaG9vZF9jbGVhbnNlZCkgJT4lCiAgc3VtbWFyaXNlKG4gPSBuKCksCiAgICAgICAgICAgIHByaWNlX21lYW4gPSBwcmljZSAlPiUgbWVhbihuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICByZXZpZXdfbWVhbiA9IHJldmlld19zY29yZXNfcmF0aW5nICU+JSBtZWFuKG5hLnJtID0gVFJVRSkpCiAgCmBgYAoKYGBge3J9Cm5laWdoYm91cmhvb2RzX3RpZHkgJTw+JQogIGxlZnRfam9pbihuZWlnaGJvcmhvb2RfYWdnLCBieSA9IGMoJ2lkJyA9ICduZWlnaGJvdXJob29kX2NsZWFuc2VkJykpCmBgYAoKTnVtYmVyIG9mIHBsYWNlcwoKYGBge3J9Cm5laWdoYm91cmhvb2RzX3RpZHkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IG4pKSArCiAgZ2VvbV9wb2x5Z29uKCkgKwogIHRoZW1lX3ZvaWQoKSArCiAgY29vcmRfbWFwKCkKYGBgClByaWNlcwoKYGBge3J9Cm5laWdoYm91cmhvb2RzX3RpZHkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IHByaWNlX21lYW4pKSArCiAgZ2VvbV9wb2x5Z29uKCkgKwogIHRoZW1lX3ZvaWQoKSArCiAgY29vcmRfbWFwKCkKYGBgClJldmlldyBzY29yZXMKCmBgYHtyfQpuZWlnaGJvdXJob29kc190aWR5ICU+JQogIGdncGxvdChhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSByZXZpZXdfbWVhbikpICsKICBnZW9tX3BvbHlnb24oKSArCiAgdGhlbWVfdm9pZCgpICsKICBjb29yZF9tYXAoKQpgYGAKCg==